`timescale 1ns / 1ps
`include "defines2.vh"
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2017/11/02 15:12:22
// Design Name: 
// Module Name: datapath
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module datapath(
	input wire clk,rst,
	//fetch stage
	output wire[31:0] pcF,
	input wire[31:0] instrF,
	//decode stage
	input wire pcsrcD,branchD,
	input wire jumpD,jalD,jrD,balD,
	output wire equalD,
	output wire[5:0] opD,functD,rtD,
	//execute stage
	input wire memtoregE,
	input wire alusrcE,regdstE,
	input wire regwriteE,
	input wire[4:0] alucontrolE,
	input mfhiE,mfloE, //yyx
	input wire [1:0] hi_mdrE,lo_mdrE,

	input wire jalE,balE,
	//mem stage
	input wire memtoregM,
	input wire regwriteM,
	output wire[31:0] aluoutM,writedataM_true,
	input wire[31:0] readdataM,
	input wire [1:0] memsizeM,
	input wire memsigndM,
	input memwriteM,
	input hi_writeM,lo_writeM, //yyx
	//writeback stage
	input wire memtoregW,
	input wire regwriteW,
	input wire [1:0] memsizeW,
	input wire memsigndW,
	input wire hi_writeW,lo_writeW, //yyx

	output wire [3:0] datawenM,

	output wire stallE,
    output wire flushE

    );

	//fetch stage
	wire [31:0] pcnextFD,pcnextbrFD,pcplus4F,pcbranchD;
	wire stallF;
	
	//decode stage
	wire [31:0] pcplus4D,instrD;
	wire forwardaD,forwardbD;
	wire [4:0] rsD,rdD;
	wire [31:0] signimmD,signimmshD;
	wire [31:0] srcaD,srca2D,srcbD,srcb2D;
	wire [4:0] saD; //yyx
	
	//execute stage
	wire [1:0] forwardaE,forwardbE;
	wire [4:0] rsE,rtE,rdE;
	wire [4:0] writeregE;
	wire [31:0] signimmE;
	wire [31:0] srcaE,srca2E,srca3E,srcbE,srcb2E,srcb3E;
	wire [31:0] aluoutE;
	wire [4:0] saE; //yyx
	wire [31:0] hi_iE, lo_iE; // E阶段的指令要写入Hilo的值
	wire [2:0] forwardhlE;//yyx
	wire start_divE,signed_divE,stall_divE;
	wire div_readyE;
	
	//mem stage
	wire [4:0] writeregM;
	wire [31:0] writedataM;
	wire [31:0] hi_iM, lo_iM; // M阶段的指令要写入Hilo的值
	
	//writeback stage
	wire [4:0] writeregW;
	wire [31:0] aluoutW,readdataW,resultW;
	
	// hilo寄存器
	wire [31:0] hi_iW, lo_iW; // W阶段的指令要写入Hilo的值
	wire [31:0] hi_oW, lo_oW; // hilo

	//hazard detection
	hazard h(
		//fetch stage
		//stallF,
		//decode stage
		rsD,rtD,
		branchD,balD,jalD,jrD,
		forwardaD,forwardbD,
		//stallD,
		//execute stage
		rsE,rtE,
		writeregE,
		regwriteE,
		memtoregE,
		forwardaE,forwardbE,
		stall_divE,
		//flushE,
		mfhiE,mfloE,
		forwardhlE,
		//mem stage
		writeregM,
		regwriteM,
		memtoregM,
		hi_writeM,lo_writeM,//yyx
		//write back stage
		writeregW,
		regwriteW,
		hi_writeW,lo_writeW,
		stallF, stallD, stallE, 
    	flushF, flushD, flushE
		);

	//next PC logic (operates in fetch an decode)
	mux2 #(32) pcbrmux(pcplus4F,pcbranchD,pcsrcD,pcnextbrFD);
	wire [31:0] pcjp,pcjr;
	assign pcjr=srca2D;
	mux2 #(32) pcmux(pcnextbrFD,
		{pcplus4D[31:28],instrD[25:0],2'b00},
		jumpD,pcjp);
	mux2 #(32) pcmux2(pcjp,
		pcjr,
		jrD,pcnextFD);

	//regfile (operates in decode and writeback)
	regfile rf(clk,regwriteW,rsD,rtD,writeregW,resultW,srcaD,srcbD);

	//############################## fetch stage ##############################
	pc #(32) pcreg(clk,rst,~stallF,pcnextFD,pcF);
	adder pcadd1(pcF,32'b100,pcplus4F);
	
	assign flushD=((branchD&&equalD)||jumpD)&&(~stallD);
	
	//############################## decode stage ##############################
	flopenr #(32) r1D(clk,rst,~stallD,pcplus4F,pcplus4D);
	flopenrc #(32) r2D(clk,rst,~stallD,flushD,instrF,instrD);
	signext se(instrD[15:0],instrD[29:28],signimmD);
	sl2 immsh(signimmD,signimmshD);
	adder pcadd2(pcplus4D,signimmshD,pcbranchD);
	wire [31:0] pcplus8D,pcplus8E;
	adder pcadd8D(pcplus4D,32'b100,pcplus8D);
	
	mux2 #(32) forwardamux(srcaD,aluoutM,forwardaD,srca2D);
	mux2 #(32) forwardbmux(srcbD,aluoutM,forwardbD,srcb2D);
	eqcmp comp(srca2D,srcb2D,opD,rtD,equalD);

	assign opD = instrD[31:26];
	assign functD = instrD[5:0];
	assign rsD = instrD[25:21];
	assign rtD = instrD[20:16];
	assign rdD = instrD[15:11];
	assign saD=instrD[10:6]; //yyx

	//############################## execute stage ##############################
	flopenrc #(32) r1E(clk,rst,~stallE,flushE,srcaD,srcaE);
	flopenrc #(32) r2E(clk,rst,~stallE,flushE,srcbD,srcbE);
	flopenrc #(32) r3E(clk,rst,~stallE,flushE,signimmD,signimmE);
	flopenrc #(5) r4E(clk,rst,~stallE,flushE,rsD,rsE);
	flopenrc #(5) r5E(clk,rst,~stallE,flushE,rtD,rtE);
	flopenrc #(5) r6E(clk,rst,~stallE,flushE,rdD,rdE);
	flopenrc #(5) r7E(clk,rst,~stallE,flushE,saD,saE);//yyx
	flopenrc #(32) r8E(clk,rst,~stallE,flushE,pcplus8D,pcplus8E);
	wire [5:0] functE;
	flopenrc #(6) r9E(clk,rst,~stallE,flushE,functD,functE);//yyx

	mux3 #(32) forwardaemux(srcaE,resultW,aluoutM,forwardaE,srca2E);
	mux3 #(32) forwardbemux(srcbE,resultW,aluoutM,forwardbE,srcb2E);
	mux2 #(32) srcbmux(srcb2E,signimmE,alusrcE,srcb3E);
	
	//乘法
	wire [63:0] mul_res;//yyx 
	mul mul(srca2E,srcb2E,functE,mul_res);

	//除法
	wire [63:0] div_res;
	wire [2:0] div_controlsE;
	assign {start_divE,signed_divE,stall_divE}=div_controlsE;
	assign div_controlsE=(functE==`DIV)?((div_readyE==1'b0)? 3'b111:3'b010):
						(functE==`DIVU)?((div_readyE==1'b0)? 3'b101:3'b000):
						3'b000;
	div div(clk,rst,signed_divE,srca2E,srcb2E,start_divE,1'b0,div_res,div_readyE);


	//hilo
	assign hi_iE = hi_mdrE == 2'b01 ? mul_res[63:32]:
				   hi_mdrE == 2'b10 ? div_res[63:32] :
               	   hi_mdrE == 2'b11 ? srca2E :
				   32'b0;//yyx
	assign lo_iE = lo_mdrE == 2'b01 ? mul_res[31:0] :
                   lo_mdrE == 2'b10 ? div_res[31:0] :
               	   lo_mdrE == 2'b11 ? srca2E :
                   32'b0; //yyx	
	// 如果是mfhi/lo指令，则ALU A应该输入hi/lo寄存器的值，B输入的是0，同时要考虑转发。
	assign srca3E = forwardhlE == 3'b000 ? srca2E :
                    forwardhlE == 3'b001 ? hi_oW :
                    forwardhlE == 3'b010 ? lo_oW :
                    forwardhlE == 3'b011 ? hi_iM :
                    forwardhlE == 3'b100 ? lo_iM :
                    forwardhlE == 3'b101 ? hi_iW :
                    forwardhlE == 3'b110 ? lo_iW :
                    32'bx;
	
	//ALU
	alu alu(srca3E,srcb3E,alucontrolE,saE,aluoutE); //yyx
	mux2 #(5) wrmux(rtE,rdE,regdstE,writeregE);
	wire [31:0] aluoutE2M;
	wire [4:0] writeregE2M;
	assign aluoutE2M=(jalE||balE)?pcplus8E:aluoutE;
	assign writeregE2M=(jalE||balE)?5'b11111:writeregE;

	
	//############################## mem stage ##############################
	flopr #(32) r1M(clk,rst,srcb2E,writedataM);
	flopr #(32) r2M(clk,rst,aluoutE2M,aluoutM);
	flopr #(5) r3M(clk,rst,writeregE2M,writeregM);
	flopr #(64) EM_hilo (clk,rst,{hi_iE, lo_iE},{hi_iM, lo_iM});
	
	//store存储数据时选择的位的信号
	wire [3:0] datawenM_;
	assign datawenM_=
		((memsizeM==2'b11)?4'b1111:
		(memsizeM==2'b10)?((aluoutM[1]==1'b0)?4'b0011:4'b1100):
		(memsizeM==2'b01)?(
			(aluoutM[1:0]==2'b00)?4'b0001:
			(aluoutM[1:0]==2'b01)?4'b0010:
			(aluoutM[1:0]==2'b10)?4'b0100:4'b1000
		):4'b0000);
	assign datawenM=memwriteM?datawenM_:4'b0000;

    //store时，传入data_ram的数据
	assign writedataM_true=
		(memsizeM==2'b11)?writedataM:
		(memsizeM==2'b10)?{2{writedataM[15:0]}}:
		(memsizeM==2'b01)?{4{writedataM[7:0]}}:
		32'b0;

	
	//############################## writeback stage ##############################
	flopr #(32) r1W(clk,rst,aluoutM,aluoutW);
	flopr #(32) r2W(clk,rst,readdataM,readdataW);
	flopr #(5) r3W(clk,rst,writeregM,writeregW);
    flopr #(64) MW_hilo (clk,rst,{hi_iM, lo_iM},{hi_iW, lo_iW});

	//load时，向寄存器中写回的数据
	wire [31:0] readdataW_true;
	wire [7:0] temp1;
	wire [15:0] temp2;
	assign temp1=(aluoutW[1:0]==2'b00)?readdataW[7:0]:
				 (aluoutW[1:0]==2'b01)?readdataW[15:8]:
				 (aluoutW[1:0]==2'b10)?readdataW[23:16]:
				 readdataW[31:24];
	assign temp2=(aluoutW[1]==1'b0)?readdataW[15:0]:
				 readdataW[31:16];
	assign readdataW_true=(memsigndW==0)?(
						  	(memsizeW==2'b11)?readdataW:
						  	(memsizeW==2'b10)?{16'b0,temp2}:
						  	(memsizeW==2'b01)?{24'b0,temp1}:
							32'b0
						  ):(
							(memsizeW==2'b11)?readdataW:
						    (memsizeW==2'b10)?{{16{readdataW[15]}},temp2}:
						    (memsizeW==2'b01)?{{24{readdataW[7]}},temp1}:
							32'b0
						  );
	hilo_reg hilo(clk,rst,hi_writeW,lo_writeW,hi_iW,lo_iW,hi_oW,lo_oW);					  
	mux2 #(32) resmux(aluoutW,readdataW_true,memtoregW,resultW);
endmodule
